#include "QcPickResultTreeModel.h"
#include <QFileInfo>
#include <QPixmap>
#include <QFileIconProvider>

QcPickResultTreeModel::KpItemData::KpItemData(const QString& path)
    : filePath(path)
{
    // 先载入缩略图，否则第一次显示会拖慢整体速度
    QPixmap pix(path);
    if (!pix.isNull()) {
        if (pix.width() > 128 || pix.height() > 128)
            pix = pix.scaled(128, 128, Qt::KeepAspectRatio);
        thumb_ = pix;
    }
}

QcPickResultTreeModel::QcPickResultTreeModel(QObject *parent)
    : QAbstractItemModel(parent)
{}

QVariant QcPickResultTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
        return QVariant();

    static const QString headerNames[] = {
        tr("名称"),
        tr("修改日期"),
        tr("大小"),
        tr("路径")
    };

    return headerNames[section];
}

QModelIndex QcPickResultTreeModel::index(int row, int column, const QModelIndex &parent) const
{
    return createIndex(row, column, parent.isValid() ? parent.row() : -1);
}

QModelIndex QcPickResultTreeModel::parent(const QModelIndex &index) const
{
    return index.internalId() == -1 ? QModelIndex() : createIndex(index.internalId(), 0, -1);
}

int QcPickResultTreeModel::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid()) // 总根节点
        return result_.empty() ? 0 : isGroupped() ? result_.size() : result_.front().items.size();

    if (!isGroupRoot(parent)) // 叶子节点
        return 0;

    // 分组根节点
    Q_ASSERT(parent.internalId() == -1);
    return result_.at(parent.row()).items.size();
}

int QcPickResultTreeModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 4; // 始终4列数据：1.名称; 2.修改日期; 3.大小; 4.路径. 详见@headerData
}

Qt::ItemFlags QcPickResultTreeModel::flags(const QModelIndex &index) const
{
    if (!this->checkIndex(index)) // NB: VERY important!!!
        return Qt::ItemFlags{};

    // 加入ItemIsUserCheckable标记，否则用户不能勾选item
    auto flag = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
    return isGroupRoot(index) ? flag : flag | Qt::ItemNeverHasChildren;
}

bool QcPickResultTreeModel::hasChildren(const QModelIndex &parent) const
{
    return rowCount(parent) > 0;
}

QVariant QcPickResultTreeModel::data(const QModelIndex &index, int role) const
{
    if (!checkIndex(index, CheckIndexOption::IndexIsValid))
        return QVariant();

    if (isGroupRoot(index)) { // 分组根节点

        // 根节点没有DecorationRole、ToolTipRole
        auto gsize = groupSize(index.row());
        if (role == Qt::DecorationRole || role == Qt::ToolTipRole || gsize == 0)
            return QVariant();

        if (role == Qt::CheckStateRole) {
            auto checkedItems = result_[index.row()].checkedItems;
            return checkedItems == 0 ? Qt::Unchecked :
                       checkedItems == gsize ? Qt::Checked : Qt::PartiallyChecked;
        }
        else { // 返回首个元素的data
            auto var = data(this->index(0, index.column(), index), role);
            if (role == Qt::DisplayRole && index.column() == 0) { // 修正group-label: 附加分组元素的数量信息
                var = var.toString() + QString(" (%1)").arg(gsize);
            }
            return var;
        }

    } // end of 分组根节点

    auto p = index.parent();
    auto group = p.row() >= 0 ? p.row() : 0;
    return fetchData_(result_.at(group).items.at(index.row()), index.column(), role);
}

bool QcPickResultTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // 只允许修改CheckStateRole
    if (role != Qt::CheckStateRole || index.column() != 0)
        return false;

    if (data(index, Qt::CheckStateRole) == value)
        return true;

    auto newState = value.toInt();

    if (isGroupRoot(index)) {
        auto group = index.row();
        if (newState == Qt::Checked) {
            result_[group].checkedItems = groupSize(group);
            setChildrenCheckStates_(group, true);
        }
        else if (newState == Qt::Unchecked) {
            result_[group].checkedItems = 0;
            setChildrenCheckStates_(group, false);
        }
    }
    else {
        if (isGroupped()) {
            auto p = index.parent();
            auto& group = result_[p.row()];
            group.items[index.row()].checkState = newState;
            if (newState == Qt::Checked)
                group.checkedItems++;
            else
                group.checkedItems--;

            emit dataChanged(p, p, { role });
        }
        else {
            result_.front().items[index.row()].checkState = newState;
        }
    }

    emit dataChanged(index, index, { role });
    return true;
}

int QcPickResultTreeModel::groupSize(int groupIdx) const
{
    if (groupIdx < 0) {
        if (result_.empty())
            return 0;

        Q_ASSERT(!isGroupped());
        return  result_.front().items.size();
    }

    return result_.at(groupIdx).items.size();
}

QModelIndex QcPickResultTreeModel::treeIndex(int group, int item, int col) const
{
    if (group < 0)
        return index(item, col);
    else if (item < 0)
        return index(group, col);
    else
        return index(item, col, index(group, 0));
}

void QcPickResultTreeModel::appendItem(int group, const QString& filePath)
{
    groupped_ = group >= 0; // TODO: 有无更好的方法???

    if (group < 0) { // 添加无分组的根节点
        if (result_.empty()) {
            KpGroupData groupData;
            groupData.items.append(KpItemData(filePath));
            result_.append(groupData);
        }
        else {
            Q_ASSERT(!isGroupped());
            result_.front().items.append(filePath);
        }
    }
    else {
        Q_ASSERT(result_.empty() || isGroupped());

        if (group < groupCount()) { // 向已有分组添加子节点
            result_[group].items.append(filePath);
        }
        else { // 添加新分组的子节点
            Q_ASSERT(group == groupCount());
            KpGroupData groupData;
            groupData.items.append(KpItemData(filePath));
            result_.append(groupData);
        }
    }
}

void QcPickResultTreeModel::removeItem(int group, int item)
{
    auto idx = treeIndex(group, item);
    beginRemoveRows(idx.parent(), idx.row(), idx.row());

    if (group < 0) {
        result_.front().items.removeAt(item);
    }
    else {
        result_[group].items.removeAt(item);
    }

    endRemoveRows();
    resetInternalData(); // ???
}

void QcPickResultTreeModel::removeGroup(int group)
{
    if (group < 0) { // clear
        beginRemoveRows(QModelIndex(), 0, groupCount() - 1);
        result_.clear();
    }
    else {
        beginRemoveRows(QModelIndex(), group, group);
        result_.removeAt(group);
    }

    endRemoveRows();
    resetInternalData(); // ???
}

void QcPickResultTreeModel::sort_(const COMP_FN& comp)
{
    // 先进行组内排序
    for (auto i = result_.begin(); i != result_.end(); i++)
        std::stable_sort(i->items.begin(), i->items.end(), comp);

    if (!isGroupped() || rowCount() < 2)
        return;

    // 再进行分组排序
    //    auto pil = this->persistentIndexList();
    //    if (pil.empty()) {
    //        std::stable_sort(result_.begin(), result_.end(),
    //           [comp](const KpGroupData& g1, const KpGroupData& g2) {
    //           return comp(g1.items.empty() ? KpItemData("") : g1.items.front(),
    //                       g2.items.empty() ? KpItemData("") : g2.items.front());
    //           });
    //  }
    //  else { // 执行persistentIndex情况下的排序

    // store the original order of indexes
    std::vector<std::pair<KpGroupData*, int>> groupSorting(result_.size());
    for (qsizetype i = 0; i < result_.size(); i++) {
        groupSorting[i].first = &result_[i];
        groupSorting[i].second = i;
    }

    // do the sorting
    auto groupComp = [comp](const std::pair<KpGroupData*, int>& g1, const std::pair<KpGroupData*, int>& g2) {
        return comp(g1.first->items.empty() ? KpItemData("") : g1.first->items.front(),
                    g2.first->items.empty() ? KpItemData("") : g2.first->items.front());
    };
    std::stable_sort(groupSorting.begin(), groupSorting.end(), groupComp);

    // change persistent index-list
    QModelIndexList fromList;
    QModelIndexList toList;
    decltype(result_) newResult(result_.size());
    for (qsizetype r = 0; r < groupSorting.size(); ++r) {
        auto& newGroup = groupSorting[r];

        // 根据排序结果交换数据
        newResult[r].checkedItems = newGroup.first->checkedItems;
        std::swap(newResult[r].items, newGroup.first->items);

        int oldRow = newGroup.second;
        if (oldRow == r) continue;

        QModelIndex from = createIndex(oldRow, 0, -1);
        //    if (std::find(pil.cbegin(), pil.cend(), from) != pil.cend()) {
        QModelIndex to = createIndex(r, 0, -1);
        fromList << from;
        toList << to;
        //    }
    }
    std::swap(result_, newResult);
    changePersistentIndexList(fromList, toList);
    //   }
}

namespace kPrivate
{
    template<typename T, typename GETTER, bool ASCEND>
    auto comp_fn = [](const T& item1, const T& item2) -> bool
    {
        GETTER g;
        if constexpr (ASCEND)
            return g(item1.filePath) < g(item2.filePath);
        else
            return g(item1.filePath) > g(item2.filePath);
    };
}

void QcPickResultTreeModel::sort(int column, Qt::SortOrder order)
{
    struct name_getter {
        auto operator()(const QString& filePath) {
            QFileInfo fi(filePath);
            return fi.baseName();
        }
    };

    struct time_getter {
        auto operator()(const QString& filePath) {
            QFileInfo fi(filePath);
            return fi.lastModified();
        }
    };

    struct size_getter {
        auto operator()(const QString& filePath) {
            QFileInfo fi(filePath);
            return fi.size();
        }
    };

    struct path_getter {
        auto operator()(const QString& filePath) {
            QFileInfo fi(filePath);
            return fi.path();
        }
    };

    if (empty()) return;

    emit layoutAboutToBeChanged({}, QAbstractItemModel::VerticalSortHint);

    if (column == 0) {
        if (order == Qt::AscendingOrder)
            sort_(kPrivate::comp_fn<KpItemData, name_getter, true>);
        else
            sort_(kPrivate::comp_fn<KpItemData, name_getter, false>);
    }
    else if (column == 1) {
        if (order == Qt::AscendingOrder)
            sort_(kPrivate::comp_fn<KpItemData, time_getter, true>);
        else
            sort_(kPrivate::comp_fn<KpItemData, time_getter, false>);
    }
    else if (column == 2) {
        if (order == Qt::AscendingOrder)
            sort_(kPrivate::comp_fn<KpItemData, size_getter, true>);
        else
            sort_(kPrivate::comp_fn<KpItemData, size_getter, false>);
    }
    else if (column == 3) {
        if (order == Qt::AscendingOrder)
            sort_(kPrivate::comp_fn<KpItemData, path_getter, true>);
        else
            sort_(kPrivate::comp_fn<KpItemData, path_getter, false>);
    }
    else {
        Q_ASSERT(false);
    }

    emit layoutChanged({}, QAbstractItemModel::VerticalSortHint);
}

bool QcPickResultTreeModel::isGroupped() const
{
    Q_ASSERT(!empty()); // 若empty，调用该方法无意义
    return groupped_;
}

bool QcPickResultTreeModel::isGroupRoot(const QModelIndex &index) const
{
    return isGroupped() && index.internalId() == -1;
}

QVariant QcPickResultTreeModel::fetchData_(const KpItemData& item, int column, int role) const
{
    if (column == 0) {
        if (role == Qt::DisplayRole) {
            return QFileInfo(item.filePath).fileName();
        }
        else if (role == Qt::CheckStateRole) {
            return item.checkState;
        }
        else if (role == Qt::UserRole) {
            return item.filePath;
        }
        else if (role == Qt::DecorationRole) {
            if (useThumbnail() && !item.thumb_.isNull())
                return item.thumb_;

            if (item.icon.isNull())
                item.icon = QFileIconProvider().icon(QFileInfo(item.filePath));
            return item.icon;
        }
        else if (role == Qt::ToolTipRole) {
            QFileInfo fi(item.filePath);
            QString tooltip;
            tooltip += QString("路径名称: ") + item.filePath + "\n";
            tooltip += QString("修改时间: ") + fi.lastModified().toString() + "\n";
            tooltip += QString("文件大小: ") + QLocale::system().formattedDataSize(fi.size(), 2, QLocale::DataSizeSIQuantifiers);
            return tooltip;
        }
    }
    else if (column == 1) { // 修改日期
        if (role == Qt::DisplayRole)
            return QFileInfo(item.filePath).lastModified().toString("yyyy/MM/dd HH::mm");
        else if (role == Qt::ToolTipRole)
            return QFileInfo(item.filePath).lastModified().toString();
    }
    else if (column == 2) { // 大小
        if (role == Qt::DisplayRole)
            return QLocale::system().formattedDataSize(QFileInfo(item.filePath).size(), 2, QLocale::DataSizeSIQuantifiers);
        else if (role == Qt::ToolTipRole)
            return QString("%1 字节").arg(QFileInfo(item.filePath).size());
    }
    else if (column == 3) { // 路径
        if (role == Qt::DisplayRole || role == Qt::ToolTipRole)
            return QFileInfo(item.filePath).canonicalPath();
    }

    return QVariant();
}

void QcPickResultTreeModel::setChildrenCheckStates_(int group, bool checked)
{
    Q_ASSERT(isGroupped());

    int newState = checked ? Qt::Checked : Qt::Unchecked;
    auto& g = result_[group];
    int childRow(0);
    for (auto iter = g.items.begin(); iter != g.items.end(); iter++, childRow++) {
        if (iter->checkState != newState) {
            iter->checkState = newState;
            auto childIndex = index(childRow, 0, index(group, 0));
            emit dataChanged(childIndex, childIndex, { Qt::CheckStateRole });
        }
    }
}
